package cn.liziguo.tank.factory;

import cn.liziguo.tank.config.GameConfig;
import cn.liziguo.tank.constants.GameEnum;
import cn.liziguo.tank.constants.ImageConst;
import cn.liziguo.tank.constants.PropertyConst;
import cn.liziguo.tank.constants.SpawnsConst;
import cn.liziguo.tank.script.HomeComponent;
import cn.liziguo.tank.util.JsonUtil;
import com.almasb.fxgl.dsl.FXGL;
import com.almasb.fxgl.entity.Entity;
import com.almasb.fxgl.entity.EntityFactory;
import com.almasb.fxgl.entity.SpawnData;
import com.almasb.fxgl.entity.Spawns;
import com.almasb.fxgl.physics.BoxShapeData;
import com.almasb.fxgl.texture.AnimatedTexture;
import com.almasb.fxgl.texture.AnimationChannel;
import javafx.geometry.Point2D;
import javafx.util.Duration;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 * @author Liziguo
 * @date 2022-07-09 15:08:42
 */
public final class MapFactory implements EntityFactory {

    /**
     * 墙
     */
    @Spawns(SpawnsConst.BRICK)
    public Entity brick(SpawnData data) {
        return FXGL.entityBuilder(data)
                .type(GameEnum.BRICK)
                .viewWithBBox("map/brick.png")
                // 可碰撞的
                .collidable()
                .neverUpdated()
                .build();
    }

    /**
     * 森林
     */
    @Spawns(SpawnsConst.GREENS)
    public Entity greens(SpawnData data) {
        Entity build = FXGL.entityBuilder(data)
                .type(GameEnum.GREENS)
                .viewWithBBox("map/greens.png")
                .collidable()
                .neverUpdated()
                .zIndex(10)
                .build();
        build.setUpdateEnabled(false);
        return build;
    }

    /**
     * 水
     */
    @Spawns(SpawnsConst.SEA)
    public Entity sea(SpawnData data) {
        AnimationChannel ac = new AnimationChannel(FXGL.image("map/sea_anim.png"), Duration.seconds(1), 2);
        AnimatedTexture at = new AnimatedTexture(ac);
        return FXGL.entityBuilder(data)
                .type(GameEnum.SEA)
                .viewWithBBox(at.loop())
                .collidable()
                .build();
    }

    /**
     * 雪地
     */
    @Spawns(SpawnsConst.SNOW)
    public Entity snow(SpawnData data) {
        return FXGL.entityBuilder(data)
                .type(GameEnum.SNOW)
                .viewWithBBox("map/snow.png")
                .collidable()
                .neverUpdated()
                .build();
    }

    /**
     * 石头
     */
    @Spawns(SpawnsConst.STONE)
    public Entity stone(SpawnData data) {
        return FXGL.entityBuilder(data)
                .type(GameEnum.STONE)
                .viewWithBBox("map/stone.png")
                .collidable()
                .neverUpdated()
                .build();
    }

    /**
     * 游戏边框
     */
    @Spawns(SpawnsConst.BORDER)
    public Entity border(SpawnData data) {
        return FXGL.entityBuilder(data)
                .type(GameEnum.BORDER)
                .bbox(new BoxShapeData(GameConfig.WIDTH, GameConfig.HEIGHT))
                .collidable()
                .neverUpdated()
                .build();
    }

    public static void createBorder() {
        // 初始化边缘
        FXGL.spawn(SpawnsConst.BORDER, new SpawnData(0, -GameConfig.HEIGHT));
        FXGL.spawn(SpawnsConst.BORDER, new SpawnData(0, GameConfig.HEIGHT));
        FXGL.spawn(SpawnsConst.BORDER, new SpawnData(-GameConfig.WIDTH, 0));
        FXGL.spawn(SpawnsConst.BORDER, new SpawnData(GameConfig.WIDTH, 0));

    }

    public static void clearHomeFence() {
        Predicate<Entity> predicate = entity -> (Boolean) entity.getPropertyOptional(PropertyConst.IS_HOME_FENCE).orElse(false);
        // 清除已存在的围墙
        List<Entity> entitiesFiltered = FXGL.getGameWorld().getEntitiesFiltered(predicate);
        entitiesFiltered.forEach(Entity::removeFromWorld);
    }

    public static Entity createHome() {
        return FXGL.entityBuilder()
                .type(GameEnum.HOME)
                .viewWithBBox(ImageConst.HOME)
                .at(GameConfig.HOME_POINT)
                .with(new HomeComponent())
                .collidable()
                // 这里设置禁用更新的话 无法通过代码启用更新
//                .neverUpdated()
                .buildAndAttach();
    }


    public static List<Entity> createHomeFence() {
        return createHomeFence(SpawnsConst.BRICK);
    }

    /**
     * 创建围墙 并指定围墙材质
     *
     * @param entityName {@link SpawnsConst}里的常量
     * @return 创建的实体
     */
    public static List<Entity> createHomeFence(String entityName) {
        List<Entity> list = new ArrayList<>(GameConfig.HOME_FENCE_POINT.size());
        // 生成围墙
        for (Point2D p : GameConfig.HOME_FENCE_POINT) {
            SpawnData data = new SpawnData(p);
            data.put(PropertyConst.IS_HOME_FENCE, true);

            Entity entity = FXGL.spawn(entityName, data);
            list.add(entity);
        }
        return list;
    }

    public static MapEntity[] loadMap(String path) {
        InputStream resourceAsStream = MapFactory.class.getClassLoader().getResourceAsStream(path);
        if (resourceAsStream == null) {
            throw new RuntimeException("关卡资源 " + path + " 不存在");
        }
        BufferedInputStream in = new BufferedInputStream(resourceAsStream);
        try {
            byte[] bytes = in.readAllBytes();
//            List<MapEntity> mapEntities = JsonUtil.parseArray(bytes, MapEntity.class);
            return JsonUtil.parse(bytes, MapEntity[].class);
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("关卡资源 " + path + " 加载失败");
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                resourceAsStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void createMap(MapEntity[] mapEntities) {
        for (int i = 0, len = mapEntities.length; i < len; i++) {
            MapEntity mapEntity = mapEntities[i];
            FXGL.spawn(mapEntity.name, mapEntity.x, mapEntity.y);
        }
    }

    public static class MapEntity {
        private String name;
        private int x;
        private int y;

        public MapEntity() {
        }

        public MapEntity(String name, double x, double y) {
            this.name = name;
            this.x = (int) x;
            this.y = (int) y;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }
    }
}
